import { CurrentRole } from '@/decorators/current.role';
import { ClientRole } from '@/modules/user/role/role.type';
import { LcDocument, LcDocumentBackup, LcProject } from '../types/editor.type';
import { PageEnum } from '@/types/enums/page.enum';
import {
  BadRequestException,
  Body,
  Controller,
  ForbiddenException,
  HttpException,
  HttpStatus,
  Post,
  UseGuards,
} from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import { Model, Types } from 'mongoose';
import { CurrentUser } from 'src/decorators/current.user';
import { UserEntity } from 'src/types/modules/user/entities/user.entity';
import { syncValidate, unallow } from 'src/utils/validate';
import { JwtAuthGuard } from '../../../../modules/user/jwt/jwt-auth.guard';
import {
  ApiEditorCloneProjectReqDto,
  ApiEditorDetailResDto,
  ApiEditorDocumentsResDto,
  ApiEditorGetDocumentReqDto,
  ApiEditorGetDocumentResDto,
  ApiEditorGetDocumentsResDto,
  ApiEditorGetHistoryReqDto,
  ApiEditorGetHistoryResDto,
  ApiEditorGetReqDto,
  ApiEditorGetResDto,
  ApiEditorImportApiReqDto,
  ApiEditorInstallModuleReqDto,
  ApiEditorInstallModuleResDto,
  ApiEditorPutDocumentDto,
} from '../types/editor.dto';
import { EditorService } from './editor.service';
import { VersionControlService } from './version-control.service';

import { DataSourceService } from '../../superType/server/dataSource.service';
import { chineseToEnglish } from '../utils/chineseToEnglish';
import { ITypeEntity } from '../../superType/types';
import {
  ApiEditorReplaceDocumentComponentsReqDto,
  ApiEditorUpdateMemberReqDto,
} from '../types/auth.dto';
import { getRepository } from 'typeorm';
import { formatPhoneNumber } from '@/types/utils/formatPhone';
import {
  ApiEditorResourceReqDto,
  ApiEditorResourceResDto,
} from '../types/resource.dto';
import { LcProjectModule } from '../types/asset.type';
import { cdnDomain } from '@/config/domain';
import { OptionalAuthGuard } from '@/modules/user/jwt/optional-auth.guard';
import { AccessControlService } from './access-control.service';
import { VFSService } from './vfs/vfs.service';

@ApiTags('Editor')
@ApiBearerAuth()
@Controller('editor')
export class EditorController {
  constructor(
    @InjectModel('project') private projectModel: Model<LcProject>,
    @InjectModel('doc') private documentModel: Model<LcDocument>,
    @InjectModel('type') private typeModel: Model<ITypeEntity>,
    @InjectModel('doc_backup')
    private documentBackupModel: Model<LcDocumentBackup>,
    private editorService: EditorService,
    private versionControlService: VersionControlService,
    private dataSourceService: DataSourceService,
    private accessControlService: AccessControlService,
    private vfsService: VFSService,
  ) {}

  @ApiOperation({ summary: '克隆项目' })
  @UseGuards(JwtAuthGuard)
  @Post('cloneProject')
  async cloneProject(
    @Body() { projectId, templateId }: ApiEditorCloneProjectReqDto,
    @CurrentUser() user: UserEntity,
    @CurrentRole() role: ClientRole,
  ) {
    const prjId = projectId || templateId;
    // 存储 RelationObjectID
    const relationIdMap: Record<string, string> = {};
    // 获取项目
    const {
      folder,
      _id,
      title,
      description,
      type,
      schema,
      packages,
      screenshot,
      width,
      height,
      clients,
      frames,
      assets,
      theme,
      themes,
      icon,
      envInfo,
      appConfig,
      documents,
      modules,
      mainComponent,
    } = await this.projectModel.findOne({
      _id: prjId,
    });
    // const newProjectI
    const newProject = new this.projectModel({
      title: projectId ? '副本-' + title : '未命名',
      description,
      type,
      schema,
      packages,
      screenshot,
      width,
      height,
      // clients,
      // frames,
      assets,
      theme,
      themes,
      icon,
      envInfo: envInfo || [],
      appConfig,
      // documents: documents.map((doc) => ({
      //   ...doc,
      //   projectId:
      // }),
      documents,
      modules,
      // mainComponent,
      teamId: role.teamId,
      userId: user.id,
      create_time: Date.now(),
      update_time: Date.now(),
    });
    newProject.validateSync({ validateBeforeSave: false });
    await newProject.save();
    relationIdMap[prjId] = newProject._id.toString();
    // 获取文档
    const _documents = await this.documentModel.find({
      projectId: prjId,
    });
    await this.documentModel.insertMany(
      _documents.map((doc) => {
        const _id = new Types.ObjectId();
        relationIdMap[doc._id] = _id.toString();
        const _doc = doc.toJSON();
        console.log('copy doc: ', doc.fileName, _doc.types?.length);
        return {
          ..._doc,
          _id,
          projectId: newProject._id,
          teamId: role.teamId,
          userId: user.id,
          create_time: Date.now(),
          update_time: Date.now(),
        };
      }),
    );
    // 获取类型
    const types = await this.typeModel.find({ projectId: prjId });
    // 更新ID
    const newTypes = types.map((type) => {
      const _id = new Types.ObjectId();
      relationIdMap[type._id] = _id.toString();
      return {
        ...type.toJSON(),
        _id,
        projectId: newProject._id,
        teamId: role.teamId,
        userId: user.id,
        create_time: Date.now(),
        update_time: Date.now(),
      };
    });
    // 替换 FkId
    newTypes.forEach((type) => {
      if (type.fkId && relationIdMap[type.fkId.toString()]) {
        type.fkId = relationIdMap[type.fkId.toString()];
      }
    });
    await this.typeModel.insertMany(newTypes);
    return newProject;
  }

  // @ApiOperation({ summary: '保存项目' })
  @UseGuards(JwtAuthGuard)
  @Post('save')
  async save(
    @Body() { _id, ...params }: LcProject,
    @CurrentUser() user: UserEntity,
    @CurrentRole() role: ClientRole,
  ) {
    // 判断用户是否有项目读写权限
    try {
      await syncValidate(params, {
        title: {
          required: true,
          enable: () => !_id,
        },
        schema: {
          type: 'object',
          required: true,
          enable: () => !_id,
        },
      });
    } catch (e) {
      console.log('[e] ', e);
      throw new HttpException(e.message, HttpStatus.BAD_REQUEST);
    }

    const data: Partial<LcProject> = unallow(params, [
      'create_time',
      'update_time',
      'allowCopy',
      'isPublic',
      'member',
      'teamId',
    ]);

    if (_id) {
      // 更新现有项目
      return this.editorService.updateProject(_id, data, user);
    } else {
      // 创建新项目
      return this.editorService.createProject(data, user.id, role.teamId);
    }
  }

  @ApiOperation({ summary: '获取项目' })
  @UseGuards(JwtAuthGuard)
  @Post('get')
  async get(
    @Body() params: ApiEditorGetReqDto,
    @CurrentUser() u: UserEntity,
    @CurrentRole() role: ClientRole,
  ): Promise<ApiEditorGetResDto> {
    const res = await this.editorService.getProject(
      params,
      u?.id,
      role?.teamId,
    );
    /**
     * 编辑状态加载项目时联查加载模块信息
     */
    if (params._id && params.mode === 'design' && res.data[0]) {
      const project = JSON.parse(JSON.stringify(res.data[0]));
      const { modules, assets } = await this.editorService.mergeModuleAssets(
        project,
        u,
        role,
      );

      // CDN域名替换
      if (assets?.length > 0) {
        assets.forEach((asset) => {
          asset.package.urls = asset.package.urls.map((url) =>
            url
              .replace('cdn.appthen.cn', cdnDomain)
              .replace('cdn.appthen.com', cdnDomain),
          );
        });
      }

      res.data[0] = {
        ...project,
        assets,
        modules,
      };
    }

    return res;
  }

  @ApiOperation({ summary: '安装/更新模块' })
  @UseGuards(JwtAuthGuard)
  @Post('installModule')
  async installModule(
    @CurrentUser() u: UserEntity,
    @CurrentRole() role: ClientRole,
    @Body() params: ApiEditorInstallModuleReqDto,
  ): Promise<ApiEditorInstallModuleResDto> {
    const { projectId, moduleIds, isDeleteExist } = params;
    const res = await this.editorService.getProject(
      {
        _id: projectId,
      },
      u.id,
      role.teamId,
    );
    const project: LcProject = JSON.parse(JSON.stringify(res.data[0]));
    const { data: moduleProjects } = await this.editorService.getProject(
      {
        _ids: moduleIds,
        isModule: true,
        pageSize: 100,
      },
      u.id,
      role.teamId,
    );

    const sourceModules = (project.modules || []).slice();
    moduleProjects.forEach((module) => {
      const { name, packageName, version } = module.storeInfo;
      const moduleInstall: LcProjectModule = {
        enable: true,
        install: true,
        projectId: module._id.toString(),
        name,
        packageName,
        version,
        installAt: Date.now(),
        installBy: u.id,
      };
      const existIndex = sourceModules.findIndex(
        (_) => _.packageName === packageName,
      );
      if (existIndex >= 0) {
        if (isDeleteExist) {
          // sourceModules.splice(existIndex, 1);
          sourceModules[existIndex].enable = !sourceModules[existIndex].enable;
        } else {
          if (sourceModules[existIndex]?.config) {
            moduleInstall.config = sourceModules[existIndex]?.config;
          }
          sourceModules[existIndex] = moduleInstall;
        }
      } else {
        sourceModules.push(moduleInstall);
      }
    });

    project.modules = sourceModules;

    /**
     * 根据更新后的Modules，重新加载模块资源
     */
    const { modules, assets } = await this.editorService.mergeModuleAssets(
      project,
      u,
      role,
    );

    // 更新项目
    const updateRes = await this.editorService.updateProject(
      projectId,
      {
        assets,
        modules,
      },
      u,
    );

    return {
      ...updateRes,
      modules,
      assets,
    };
  }

  @ApiOperation({ summary: '获取项目详情' })
  @UseGuards(OptionalAuthGuard)
  @Post('detail')
  async detail(
    @Body() params: ApiEditorGetReqDto,
    @CurrentUser() u: UserEntity,
    @CurrentRole() role: ClientRole,
  ): Promise<ApiEditorDetailResDto> {

    // 🆕 权限检查：检查用户是否有查看项目的权限
    const hasPermission = await this.accessControlService.checkPermission(
      u?.id, // 支持匿名用户
      {
        projectId: params._id || params.projectId, // 使用 _id 或 projectId
        path: '/', // 项目根路径
      },
      'view',
    );


    if (!hasPermission.allowed) {
      throw new ForbiddenException(
        hasPermission.reason || '您没有权限查看此项目',
      );
    }

    const res = await this.editorService.getProjectAndDocuments(
      params,
      false,
      u,
      role,
    );
    return res;
  }

  @ApiOperation({ summary: '获取文档(拉取依赖文档) - 支持版本控制' })
  @UseGuards(OptionalAuthGuard)
  @Post('getDocuments')
  async getDocuments(
    @Body()
    params: ApiEditorGetReqDto & {
      version?: string; // 🆕 可选版本参数：'dev' | 'release' | 'v1.0.0' 等
    },
    @CurrentUser() u: UserEntity,
    @CurrentRole() role: ClientRole,
  ): Promise<ApiEditorGetDocumentsResDto> {
    const { version, projectId, ...originalParams } = params;
    // 🆕 版本控制逻辑
    if (version && version !== 'dev') {
      // 检查项目权限
      const project = await this.projectModel.findById(projectId);
      if (!project) {
        throw new BadRequestException('项目不存在');
      }

      // 🆕 使用新的权限系统检查访问权限
      const hasPermission = await this.accessControlService.checkPermission(
        u?.id, // 支持匿名用户
        {
          projectId: projectId,
          path: '/', // 项目根路径
        },
        'view',
      );

      if (!hasPermission.allowed) {
        throw new ForbiddenException(
          hasPermission.reason || '您无权访问该项目',
        );
      }

      try {
        let versionFiles;

        if (version === 'release') {
          // 获取当前发布版本
          versionFiles = await this.versionControlService.getReleaseFiles(
            projectId,
          );
        } else {
          // 获取指定版本（如 v1.0.0）
          versionFiles = await this.versionControlService.getReleaseFiles(
            projectId,
            version,
          );
        }

        // 转换为 getDocuments 的返回格式
        return {
          data: versionFiles.documents || [],
          types: versionFiles.types || [],
          project: versionFiles.project,
          total: versionFiles.documents?.length || 0,
          version: version, // 🆕 返回版本信息
        } as any;
      } catch (error) {
        // 如果没有发布版本，fallback 到开发版本
        if (version === 'release' && error.message.includes('没有发布版本')) {
          // 继续执行下面的开发版本逻辑
        } else {
          throw error;
        }
      }
    }

    // 🆕 使用新的权限系统检查是否有查看源码的权限
    const hasSourceViewPermission =
      await this.accessControlService.checkPermission(
        u?.id,
        {
          projectId: projectId,
          path: '/', // 检查项目根路径的源码查看权限
        },
        'source:view',
      );

    // 默认获取开发版本（原有逻辑）
    const res = await this.editorService.getDocuments(
      { projectId, ...originalParams },
      false,
      u,
      role,
    );

    // 🆕 如果用户没有源码查看权限，隐藏源代码
    if (!hasSourceViewPermission.allowed) {
      // 清理文档中的源代码信息
      res.documents?.forEach((doc) => {
        if (doc.schema) {
          // 保留基本的页面结构，但移除敏感的源代码信息
          // 这里可以根据需要具体实现
          console.log('隐藏源代码:', doc.fileName);
        }
      });
    }
    return res;
  }

  @ApiOperation({ summary: '获取历史记录' })
  @UseGuards(JwtAuthGuard)
  @Post('getHistory')
  async getHistory(
    @Body() params: ApiEditorGetHistoryReqDto,
    @CurrentUser() u: UserEntity,
    @CurrentRole() role: ClientRole,
  ): Promise<ApiEditorGetHistoryResDto> {
    const { docId, projectId } = params;
    // @TODO: 这里需要鉴权用户是否有项目编辑权限
    const data = await this.documentBackupModel.find({
      docId,
      projectId,
    });
    return {
      data,
    };
  }

  @ApiOperation({ summary: '获取资源' })
  @UseGuards(JwtAuthGuard)
  @Post('resource')
  async resource(
    @Body() params: ApiEditorResourceReqDto,
    @CurrentUser() u: UserEntity,
    @CurrentRole() role: ClientRole,
  ): Promise<ApiEditorResourceResDto> {
    return this.editorService.getResource(params, u, role);
  }

  @ApiOperation({ summary: '获取文档依赖' })
  @UseGuards(JwtAuthGuard)
  @Post('getDocumentAndImport')
  async getDocumentAndImport(
    @Body() params: ApiEditorGetReqDto,
    @CurrentUser() u: UserEntity,
    @CurrentRole() role: ClientRole,
  ): Promise<ApiEditorDocumentsResDto> {
    return this.editorService.getDocuments(params);
  }

  // @ApiOperation({ summary: '获取链接' })
  // @UseGuards(JwtAuthGuard)
  // @Post('getDocumentAndImport')
  // async getDocumentAndImport(
  //   @Body() params: ApiEditorGetReqDto,
  //   @CurrentUser() u: UserEntity,
  //   @CurrentRole() role: ClientRole,
  // ): Promise<ApiEditorDocumentsResDto> {
  //   return this.editorService.getDocuments(params);
  // }

  @ApiOperation({ summary: '获取项目导出详情' })
  @UseGuards(JwtAuthGuard)
  @Post('getExportDetail')
  async getExportDetail(
    @Body() params: ApiEditorGetReqDto,
    @CurrentUser() u: UserEntity,
    @CurrentRole() role: ClientRole,
  ): Promise<ApiEditorDetailResDto> {
    return this.editorService.getProjectAndDocuments(params, true, u, role);
  }

  // @ApiOperation({ summary: '删除项目' })
  @UseGuards(JwtAuthGuard)
  @Post('remove')
  async remove(
    @CurrentUser() u: UserEntity,
    @CurrentRole() role: ClientRole,
    @Body() params: any,
  ) {
    const { _id } = params;
    return this.projectModel.deleteOne({
      _id,
    }) as any;
  }

  @ApiOperation({ summary: '保存文档' })
  @UseGuards(JwtAuthGuard)
  @Post('putDocument')
  async putDocument(
    @Body() body: ApiEditorPutDocumentDto,
    @CurrentUser() user: UserEntity,
    @CurrentRole() role: ClientRole,
  ) {

    // 调用 service 层的保存方法
    return this.editorService.saveDocument(
      body,
      {
        userId: user.id,
        teamId: role.teamId,
      },
    );
  }

  @ApiOperation({ summary: '文档' })
  @Post('getDocument')
  async getDocument(
    @Body() params: ApiEditorGetDocumentReqDto,
  ): Promise<ApiEditorGetDocumentResDto> {
    return this.editorService.getDocument(params);
  }

  // @ApiOperation({ summary: '删除文档' })
  @UseGuards(JwtAuthGuard)
  @Post('removeDocument')
  async removeDocument(
    @CurrentUser() u: UserEntity,
    @CurrentRole() role: ClientRole,
    @Body() params: any,
  ) {
    const { _id } = params;
    // 获取文档
    const document = await this.documentModel.findById(_id);
    if (!document) {
      throw new BadRequestException('文档不存在');
    }
    const havePermission = this.editorService.checkAuth(
      document.projectId,
      'edit',
      u.id,
    );
    if (!havePermission) {
      throw new BadRequestException('您无权操作');
    }
    return this.documentModel.deleteOne({
      _id,
    }) as any;
  }

  @ApiOperation({ summary: '中文转英文' })
  @UseGuards(JwtAuthGuard)
  @Post('transformChinese')
  async transformChinese(
    @CurrentUser() u: UserEntity,
    @CurrentRole() role: ClientRole,
    @Body()
    params: {
      text: string;
    },
  ) {
    return chineseToEnglish(params.text);
  }

  @ApiOperation({ summary: '引入接口文档' })
  @UseGuards(JwtAuthGuard)
  @Post('importApiDoc')
  async importApiDoc(
    @CurrentUser() u: UserEntity,
    @CurrentRole() role: ClientRole,
    @Body() params: ApiEditorImportApiReqDto,
  ) {
    // try {
    //   const result = await SwaggerParser.parse(apiJSON as any);
    //   return result;
    // } catch (err) {
    //   console.error(err);
    // }
    // return this.editorService.transformCode(params);
    return this.dataSourceService.importTypesFromOpenapi(
      {
        docItem: params.typeItem,
      },
      u.id,
      role.teamId,
    );
  }

  @ApiOperation({ summary: '获取项目成员' })
  @UseGuards(JwtAuthGuard)
  @Post('getMemberInfo')
  async getMemberInfo(
    @CurrentUser() u: UserEntity,
    @CurrentRole() role: ClientRole,
    @Body() params: ApiEditorUpdateMemberReqDto,
  ) {
    const { projectId } = params;
    const project = await this.projectModel.findById(projectId);
    if (!project) throw new BadRequestException('project not exist');
    const userIds = (project.member || []).map((_) => _.userId);
    userIds.push(project.userId);
    const users = await getRepository(UserEntity)
      .createQueryBuilder('u')
      .where('u.id in (:...userIds)', { userIds })
      .select(`u.name,u.avatar,u.phone,u.id`)
      .getRawMany();
    const map: Record<number, UserEntity> = {};
    users.forEach((_) => {
      map[_.id] = _;
    });
    project.member = [
      {
        userId: project.userId,
        documentPermission: 'edit',
        documentRole: 'owner',
      },
      ...(project.member || []),
    ];
    return (project.member || [])
      .filter((_) => !!map[_.userId]?.id)
      .map((member) => {
        return {
          ...member,
          name:
            map[member.userId].name ||
            formatPhoneNumber(String(map[member.userId].phone)),
          avatar: map[member.userId].avatar,
        };
      });
  }

  @ApiOperation({ summary: '更新项目成员' })
  @UseGuards(JwtAuthGuard)
  @Post('updateMember')
  async updateMember(
    @CurrentUser() u: UserEntity,
    @CurrentRole() role: ClientRole,
    @Body() params: ApiEditorUpdateMemberReqDto,
  ) {
    const { projectId, action, member, phone } = params;
    const project = await this.projectModel.findById(projectId);
    if (!project) throw new BadRequestException('project not exist');
    // 简单的判断
    if (project.userId !== u.id) {
      throw new BadRequestException('您不是项目所有人');
    }
    const projectMember = project.member || [];
    // 鉴权
    const havePermission = await this.editorService.checkAuth(
      project,
      'updateMember',
      u.id,
    );
    if (!havePermission) {
      throw new BadRequestException('您无权操作');
    }
    const existMemberIndex = projectMember.findIndex(
      (_) => _.userId === member.userId,
    );
    if (action === 'add') {
      const user = await getRepository(UserEntity).findOne({
        where: {
          phone,
        },
        select: ['name', 'id', 'avatar'],
      });
      const existMemberIndex = projectMember.findIndex(
        (_) => _.userId === user.id,
      );
      if (!user?.id) {
        throw new BadRequestException('用户不存在，请确认此用户已注册');
      }
      if (user?.id === project.userId)
        throw new BadRequestException('成员已存在');
      if (existMemberIndex >= 0) throw new BadRequestException('成员已存在');
      if (!member.documentPermission) throw new BadRequestException('参数错误');
      projectMember.push({
        userId: user.id,
        documentPermission: member.documentPermission,
        documentRole:
          member.documentPermission === 'view' ? 'onlooker' : 'member',
        inviteAt: Date.now(),
        inviteBy: u.id,
      });
    }
    if (action === 'update') {
      if (existMemberIndex < 0) throw new BadRequestException('成员不存在');
      projectMember[existMemberIndex] = member;
    }
    if (action === 'remove') {
      projectMember.splice(existMemberIndex, 1);
    }
    // 落库
    const updateRes = await this.projectModel.updateOne(
      { _id: projectId },
      {
        member: projectMember,
      },
    );
    return projectMember;
  }

  @ApiOperation({ summary: '更新文档依赖' })
  @UseGuards(JwtAuthGuard)
  @Post('replaceDocumentComponents')
  async replaceDocumentComponents(
    @CurrentUser() u: UserEntity,
    @CurrentRole() role: ClientRole,
    @Body() params: ApiEditorReplaceDocumentComponentsReqDto,
  ): Promise<void> {
    const { projectId, asset } = params;
    const { data: projects } = await this.editorService.getProject(
      {
        _id: projectId,
      },
      u.id,
      role.teamId,
    );
    const project = projects[0];
    const { assets, documents } = project;
    for (let i = 0; i < documents.length; i++) {
      const doc = documents[i];
      const { componentsMap, componentsRoot } = doc;
      // @TODO: 更新没被Asset替换的package导致无法打开项目的问题
    }
  }

  @ApiOperation({ summary: '获取项目所有资源' })
  @UseGuards(JwtAuthGuard)
  @Post('getAllResources')
  async getAllResources(
    @CurrentUser() u: UserEntity,
    @CurrentRole() role: ClientRole,
    @Body() params: { projectId: string; folder?: string },
  ) {
    console.log('getAllResources 参数', params);
    return this.editorService.getAllProjectResources({
      ...params,
      userId: u.id,
    });
  }

  // 版本控制相关接口
  // 🆕 版本控制接口已移至 VersionControlController

  // 🆕 所有版本控制相关接口已移至 VersionControlController
  // 包括：createCommit, getCommitHistory, branchOperation, tagOperation, compareFiles 等

  // 🆕 模块安装接口已移至专门的模块服务

  @ApiOperation({ summary: '比较两个项目目录资源差异' })
  @UseGuards(JwtAuthGuard)
  @Post('compareProjectFolders')
  async compareProjectFolders(
    @CurrentUser() u: UserEntity,
    @CurrentRole() role: ClientRole,
    @Body()
    params: {
      sourceProjectId: string;
      targetProjectId: string;
      sourceFolder?: string;
      targetFolder?: string;
    },
  ) {
    const { sourceProjectId, targetProjectId } = params;

    // 检查源项目权限
    const sourceProject = await this.projectModel.findById(sourceProjectId);
    if (!sourceProject) {
      throw new BadRequestException('源项目不存在');
    }
    console.log('sourceProject.title', sourceProject.title, u.id);
    const sourcePermission = await this.editorService.checkAuth(
      sourceProject,
      'view',
      u.id,
    );
    console.log('sourcePermission: ', sourcePermission);
    if (!sourcePermission) {
      throw new BadRequestException('您无权访问源项目');
    }

    // 检查目标项目权限
    const targetProject = await this.projectModel.findById(targetProjectId);
    if (!targetProject) {
      throw new BadRequestException('目标项目不存在');
    }
    const targetPermission = await this.editorService.checkAuth(
      targetProject,
      'view',
      u.id,
    );
    if (!targetPermission) {
      throw new BadRequestException('您无权访问目标项目');
    }

    return this.editorService.compareProjectFolders(params);
  }

  @ApiOperation({ summary: '生成文件快照数组 - 用于版本提交' })
  @UseGuards(JwtAuthGuard)
  @Post('generateFileSnapshots')
  async generateFileSnapshots(
    @CurrentUser() u: UserEntity,
    @CurrentRole() _role: ClientRole,
    @Body()
    params: {
      projectId: string;
      folder?: string;
      branch?: string;
    },
  ) {
    const { projectId } = params;

    // 检查项目权限
    const project = await this.projectModel.findById(projectId);
    if (!project) {
      throw new BadRequestException('项目不存在');
    }
    const hasPermission = await this.editorService.checkAuth(
      project,
      'view',
      u.id,
    );
    if (!hasPermission) {
      throw new BadRequestException('您无权访问该项目');
    }

    return this.editorService.generateFileSnapshots({
      ...params,
      userId: u.id,
    });
  }

  @ApiOperation({ summary: '比较文件快照数组 - 获取变更文件列表' })
  @UseGuards(JwtAuthGuard)
  @Post('compareFileSnapshots')
  async compareFileSnapshots(
    @CurrentUser() _u: UserEntity,
    @CurrentRole() _role: ClientRole,
    @Body()
    params: {
      sourceSnapshots: any[]; // FileSnapshot[]
      targetSnapshots: any[]; // FileSnapshot[]
      sourceLabel?: string;
      targetLabel?: string;
      mode?: 'bidirectional' | 'unidirectional';
    },
  ) {
    return this.editorService.compareFileSnapshots(params);
  }
}
